import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.Arrays;
import java.util.Random;

public class Game2048 extends JPanel {  // 此java文件的public类为Game2048
    private static final int SIZE = 4;
    private static final int SQUARE_A = 100;
    private int[][] grid = new int[SIZE][SIZE];
    private final Random random = new Random();
    private boolean win = false;
    private boolean lose = false;

    Game2048() {
        setFocusable(true);
        // 生成两个随机值，填入grid
        addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                super.keyPressed(e);
                if (!win && !lose) {
                    switch (e.getKeyCode()) {
                        // case KeyEvent.VK_LEFT -> System.out.println("左");
                        // ...
                        // case KeyEvent.VK_UP -> System.out.println("下");
                        case KeyEvent.VK_LEFT -> moveLeft();
                        case KeyEvent.VK_RIGHT -> moveRight();
                        case KeyEvent.VK_UP -> moveUp();
                        case KeyEvent.VK_DOWN -> moveDown();
                    }
                    repaint();  // 刷新
                    spawnTile();
                    checkGameState();
                }
            }
        });
        spawnTile();
        spawnTile();
    }

    private void moveLeft() {
        for (int row = 0; row < SIZE; row++) {
            int[] mergedRow = new int[SIZE];
            Arrays.fill(mergedRow, 0);
            boolean[] alreadyMerged = new boolean[SIZE];
            Arrays.fill(alreadyMerged, false);
            int colIndex = 0;   // 答案数组中的第几个
            for (int col = 0; col < SIZE; col++) {
                if (grid[row][col] != 0) {
                    if (colIndex == 0) {
                        mergedRow[colIndex] = grid[row][col];
                        colIndex++;
                    } else {
                        if (grid[row][col] == mergedRow[colIndex - 1] && !alreadyMerged[colIndex - 1]) {
                            mergedRow[colIndex - 1] *= 2;
                            alreadyMerged[colIndex] = true;
                        } else {
                            mergedRow[colIndex] = grid[row][col];
                            colIndex++;
                        }
                    }
                }
            }
            System.arraycopy(mergedRow, 0, grid[row], 0, SIZE);
        }
    }

    private void moveRight() {
        // 转180度
        rotateRight();
        rotateRight();
        moveLeft();
        // 转180度
        rotateRight();
        rotateRight();
    }

    private void moveUp() {
        // 转270度
        rotateRight();
        rotateRight();
        rotateRight();
        moveLeft();
        // 转90度
        rotateRight();
    }

    private void moveDown() {
        // 转90度
        rotateRight();
        moveLeft();
        // 转270度
        rotateRight();
        rotateRight();
        rotateRight();
    }

    private void rotateRight() {
        int[][] newGrid = new int[SIZE][SIZE];
        for (int row = 0; row < SIZE; row++) {
            for (int col = 0; col < SIZE; col++) {
                newGrid[col][SIZE - 1 - row] = grid[row][col];
            }
        }
        grid = newGrid;
    }

    // 判断游戏胜利与否
    private void checkGameState() {
        int cnt = 0;
        for (int row = 0; row < SIZE; row++) {
            for (int col = 0; col < SIZE; col++) {
                if (grid[row][col] == 2048) {
                    win = true;
                    break;
                } else {
                    if (grid[row][col] != 0) {
                        cnt++;
                    }
                }
            }
            if (win)
                break;
        }
        if (!win) {
            if (cnt < SIZE * SIZE) {
                return;
            }
            for (int row = 0; row < SIZE; row++) {
                for (int col = 0; col < SIZE; col++) {
                    if (row < SIZE - 1 && grid[row][col] == grid[row + 1][col]) {
                        return;
                    }
                    if (col < SIZE - 1 && grid[row][col] == grid[row][col + 1]) {
                        return;
                    }
                }
            }
            lose = true;
        }
    }

    private void spawnTile() {
        int cnt = 0;
        for (int row = 0; row < SIZE; row++) {
            for (int col = 0; col < SIZE; col++) {
                if (grid[row][col] != 0) {
                    cnt++;
                }
            }
        }
        if (cnt == SIZE * SIZE) {
            return;
        }
        int x, y;
        do {
            x = random.nextInt(SIZE);
            y = random.nextInt(SIZE);
        } while (grid[x][y] != 0);
        grid[x][y] = random.nextInt(10) < 5 ? 2 : 4;
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.setColor(Color.LIGHT_GRAY);
        // g.fillRect(0,0,420,460);
        g.fillRect(0, 0, getWidth(), getHeight()); // 自动匹配宽度和高度
        for (int row = 0; row < 4; row++) {
            for (int col = 0; col < 4; col++) {
                // 画小正方形
                drawTile(g, grid[row][col], col * SQUARE_A, row * SQUARE_A);
            }
        }
        // g.drawRect();
        // g.fillRect();
        g.setColor(Color.BLACK);
        if (win) {
            g.drawString("You Win!", getWidth() / 2, getHeight() / 2);
        }
        if (lose) {
            g.drawString("Game Over!", getWidth() / 2, getHeight() / 2);
        }
    }

    private void drawTile(Graphics g, int value, int x, int y) {
        // RGB (0,255)
        int m = (int) (Math.log(value) / Math.log(2));
        g.setColor(value == 0 ? Color.GRAY : new Color(255 - m * 20, 240, 200));
        g.fillRect(x, y, SQUARE_A, SQUARE_A);
        // 边框的颜色
        g.setColor(Color.BLACK);
        g.drawRect(x, y, SQUARE_A, SQUARE_A);

        if (value != 0) {
            FontMetrics fm = g.getFontMetrics();
            fm.getHeight();
            int textX = x + (SQUARE_A - fm.stringWidth(String.valueOf(value))) / 2;
            int textY = y + (SQUARE_A - fm.getHeight()) / 2 + fm.getAscent();
            g.drawString(String.valueOf(value), textX, textY);
        }
    }

    public static void main(String[] args) {
        JFrame frame = new JFrame("2048 Game");
        Game2048 game2048 = new Game2048();
        frame.setVisible(true);
        frame.setSize(420, 460);
        frame.add(game2048);

        // System.out.println("Hello world!");
    }
}